package com.sneaker.shower.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.sneaker.shower.common.BizException;
import com.sneaker.shower.common.ThreadLocalCache;
import com.sneaker.shower.entity.domain.menu.ProductDO;
import com.sneaker.shower.entity.domain.message.SessionDO;
import com.sneaker.shower.entity.domain.order.GoodsItemDO;
import com.sneaker.shower.entity.domain.order.PayOrderDO;
import com.sneaker.shower.entity.domain.order.SubOrderDO;
import com.sneaker.shower.entity.domain.order.discount.CouponDO;
import com.sneaker.shower.entity.domain.organization.OrganizationDO;
import com.sneaker.shower.entity.domain.user.UserDO;
import com.sneaker.shower.entity.domain.user.UserLevelDO;
import com.sneaker.shower.entity.domain.user.UserReceiveInfoDO;
import com.sneaker.shower.entity.dto.*;
import com.sneaker.shower.entity.dto.coupon.CouponAddReqDTO;
import com.sneaker.shower.entity.dto.coupon.CouponRespDTO;
import com.sneaker.shower.entity.enums.KuaiDiCompanyEnum;
import com.sneaker.shower.entity.enums.OrderDiscountTypeEnum;
import com.sneaker.shower.entity.enums.SubOrderStatusEnum;
import com.sneaker.shower.entity.enums.UserTypeEnum;
import com.sneaker.shower.mapper.SubOrderMapper;
import com.sneaker.shower.mapstruct.GoodsItemMapStruct;
import com.sneaker.shower.mapstruct.PayOrderMapStruct;
import com.sneaker.shower.mapstruct.SubOrderMapStruct;
import com.sneaker.shower.service.*;
import com.sneaker.shower.util.JacksonUtils;
import com.sneaker.shower.util.ValidateUserUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
public class SubOrderServiceImpl extends ServiceImpl<SubOrderMapper, SubOrderDO> implements SubOrderService {

    private final static String CLEAN_STRING = "SNEAKER CLEANING球鞋精洗";
    private final static String REPAIR_STRING = "SNEAKER REPAIRING球鞋修复";
    private final static String CLEAN_IMAGE = "https://sneakershower.club/gateway/file/download?file=52f642ed1d3b4a4b9d2289eba6890bd8.png";
    private final static String REPAIR_IMAGE = "https://sneakershower.club/gateway/file/download?file=7b1e62e543804533a8a363efc0521bf3.png";
    private final GoodsItemService goodsItemService;
    private final MenuService menuService;
    private final ProductService productService;
    private final KefuService keFuService;
    private final KuaiDiService kuaiDiService;
    private final UserLevelService userLevelService;
    private final UserService userService;
    private final PayService payService;
    private final WxMaPortalService wxMaPortalService;
    private final UserReceiveInfoService userReceiveInfoService;
    private final OrganizationService organizationService;
    private final CouponService couponService;
    private final OrderDiscountService orderDiscountService;
    @Value("${orderChangeFromId}")
    private String ORDER_CHANGE_FROM_ID;
    @Value("${sendMsgFromId}")
    private String SEND_MSG_FROM_ID = "6R0xC0bSLqweWorYivJ-l-wR389RcBIfkD830n5Za1s";

    @Autowired
    public SubOrderServiceImpl(GoodsItemService goodsItemService,
                               MenuService menuService,
                               KuaiDiService kuaiDiService,
                               ProductService productService,
                               KefuService keFuService,
                               UserLevelService userLevelService,
                               PayService payService,
                               UserService userService,
                               WxMaPortalService wxMaPortalService,
                               UserReceiveInfoService userReceiveInfoService,
                               OrganizationService organizationService,
                               CouponService couponService,
                               OrderDiscountService orderDiscountService) {
        this.goodsItemService = goodsItemService;
        this.menuService = menuService;
        this.productService = productService;
        this.kuaiDiService = kuaiDiService;
        this.keFuService = keFuService;
        this.userLevelService = userLevelService;
        this.payService = payService;
        this.userService = userService;
        this.wxMaPortalService = wxMaPortalService;
        this.userReceiveInfoService = userReceiveInfoService;
        this.organizationService = organizationService;
        this.couponService = couponService;
        this.orderDiscountService = orderDiscountService;
    }

    @Override
    public void cartSubmit(SubOrderGuidSCommitDTO subOrderGuidSCommitDTO) {
//        if (subOrderGuidSCommitDTO == null || CollectionUtils.isEmpty(subOrderGuidSCommitDTO.getSubOrderGuidList())) {
//            log.info("购物车下单请求入参：{}为空", JacksonUtils.writeValueAsString(subOrderGuidSCommitDTO));
//            return;
//        }
//        List<String> subOrderGuidList = subOrderGuidSCommitDTO.getSubOrderGuidList();
//        SubOrder updateOrderKuaidi = SubOrder.builder().isInCart(false).cartGuid(null).build();
//        update(updateOrderKuaidi, new LambdaQueryWrapper<SubOrder>().in(SubOrder::getGuid, subOrderGuidList));
    }

    @Override
    public Boolean submit(SubOrderDTO subOrderDTO, Long cartGuid, Boolean isInCart, Byte orderState, Integer type) {
        UserDTO userDTO = ThreadLocalCache.get();
        List<GoodsItemDTO> goodsItemDTOList = subOrderDTO.getGoodsItemDTOList();
        if (CollectionUtils.isEmpty(goodsItemDTOList)) {
            throw new BizException("当前订单未选择产品");
        }
        Long subOrderGuid = IdWorker.getId();
        List<GoodsItemDO> goodsItemDOS = GoodsItemMapStruct.INSTANCE.dtoList2DOList(goodsItemDTOList);
        goodsItemDOS.forEach(goodsItem -> {
            ProductDO byId = productService.getById(goodsItem.getProductGuid());
            goodsItem.setSubOrderGuid(subOrderGuid);
            goodsItem.setGuid(IdWorker.getId());
            goodsItem.setUserGuid(Long.parseLong(userDTO.getGuid()));
            goodsItem.setOrganizationGuid(Long.parseLong(userDTO.getOrganizationGuid()));
            goodsItem.setProductName(byId.getRealName());
        });
        boolean goodsSave = goodsItemService.saveBatch(goodsItemDOS);
        if (!goodsSave) {
            throw new BizException("下单失败，请稍后重试");
        }
        SubOrderDO subOrderDO = SubOrderMapStruct.INSTANCE.dto2DO(subOrderDTO);
        subOrderDO.setOrganizationGuid(Long.parseLong(userDTO.getOrganizationGuid()));
        subOrderDO.setCartGuid(cartGuid);
        subOrderDO.setIsInCart(isInCart);
        subOrderDO.setGuid(subOrderGuid);
        subOrderDO.setOrderPrice(goodsItemDOS.stream().map((e) -> e.getPrice() == null ? BigDecimal.ZERO : e.getPrice()).reduce(BigDecimal::add).orElse(BigDecimal.ZERO));
        subOrderDO.setStatus(orderState);
        subOrderDO.setUserGuid(Long.parseLong(userDTO.getGuid()));
        String s1 = goodsItemDOS.stream().map(GoodsItemDO::getProductName).reduce((s, s2) -> s + " + " + s2).orElse("");
        subOrderDO.setProductName(s1);
        if (Objects.equals(0, type)) {
            subOrderDO.setOrderName(CLEAN_STRING);
            subOrderDO.setImageUrl(CLEAN_IMAGE);
        } else {
            subOrderDO.setOrderName(REPAIR_STRING);
            subOrderDO.setImageUrl(REPAIR_IMAGE);
        }
        boolean save = save(subOrderDO);
        if (!save) {
            goodsItemService.remove(new LambdaQueryWrapper<GoodsItemDO>().eq(GoodsItemDO::getSubOrderGuid, subOrderGuid));
            save = false;
        }
        return save;
    }

    @Override
    public void commit(SubOrderDTO subOrderDTO) {
        if (StringUtils.isAllEmpty(subOrderDTO.getImg1(), subOrderDTO.getImg2(), subOrderDTO.getImg3(), subOrderDTO.getImg4(), subOrderDTO.getImg5(), subOrderDTO.getImg6())) {
            throw new BizException("请至少上传一张图片");
        }
        if (StringUtils.isEmpty(subOrderDTO.getReceiveGuid())) {
            throw new BizException("收货地址不能为空");
        }
        SubOrderDO db = getById(Long.parseLong(subOrderDTO.getGuid()));
        if (db == null) {
            throw new BizException("订单不存在");
        }
        if (db.getStatus() >= SubOrderStatusEnum.WAIT_FOR_TRACK.getCode()) {
            throw new BizException("订单已提交，请勿重复提交");
        }
        SubOrderDO subOrderDO = SubOrderMapStruct.INSTANCE.dto2DO(subOrderDTO);
        subOrderDO.setReceiveInfoGuid(subOrderDTO.getReceiveGuid());
        subOrderDO.setStatus(SubOrderStatusEnum.WAIT_FOR_TRACK.getCode());
        subOrderDO.setIsInCart(false);
        subOrderDO.setCartGuid(null);
        updateById(subOrderDO);
        Long orderGuid = subOrderDO.getGuid();
        SubOrderDO byId = getById(subOrderDO.getGuid());
        List<GoodsItemDO> list = goodsItemService.list(new LambdaQueryWrapper<GoodsItemDO>().eq(GoodsItemDO::getSubOrderGuid, orderGuid));
        BigDecimal totalPrice = byId.getOrderPrice();
        String totalPriceStr = "预计 " + totalPrice + " 元";
        String productName = list.get(0).getProductName() + "等...";
        String createTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        String[] args = {String.valueOf(orderGuid), productName, totalPriceStr, createTime};
        keFuService.sendMessage("order", String.valueOf(orderGuid), args, 0);
    }

    @Override
    @Transactional
    /**
     * orderPrice 订单金额
     * totalFee 应付金额
     * cardFee 余额支付金额
     * discount 会员折扣
     * totalDiscountFee 优惠金额
     * discountFee 优惠券金额
     * actuallyPayFee 实付金额
     *
     * orderPrice = totalFee + totalDiscountFee
     * totalDiscountFee = orderPrice - ((orderPrice - discountFee) * discount)
     * totalFee = (orderPrice - discountFee) * discount
     * totalFee = actuallyPayFee + cardFee
     *
     */
    public TryPayRespDTO tryPay(PrepareToPayReqDTO prepareToPayReqDTO) {
        UserDTO userDTO = ThreadLocalCache.get();
        long orderGuid = Long.parseLong(prepareToPayReqDTO.getOrderGuid());
        SubOrderDO subOrderDO = this.getById(orderGuid);
        if (Objects.equals(SubOrderStatusEnum.PAYING.getCode(), subOrderDO.getStatus())) {
            throw new BizException("订单正在支付中，请勿重复支付");
        }
        if (!Objects.equals(SubOrderStatusEnum.PAY_FAILED.getCode(), subOrderDO.getStatus())
                && subOrderDO.getStatus() >= SubOrderStatusEnum.PAY_SUCCESS.getCode()) {
            throw new BizException("订单已支付成功，请勿重复支付");
        }
        subOrderDO.setStatus(SubOrderStatusEnum.PAYING.getCode());
        boolean updateSubOrder = updateById(subOrderDO);
        if (!updateSubOrder) {
            throw new BizException("订单状态已发生变化  请重新拉取订单信息");
        }
        PayOrderDO payOrderDO = new PayOrderDO();
        payOrderDO.setGuid(IdWorker.getId());
        payOrderDO.setUserGuid(Long.parseLong(userDTO.getGuid()));
        payOrderDO.setOrganizationGuid(subOrderDO.getOrganizationGuid());
        payOrderDO.setType(Byte.valueOf("0"));
        payOrderDO.setAttach(String.valueOf(orderGuid));
        payOrderDO.setOrderPrice(subOrderDO.getOrderPrice());
        payOrderDO.setVersion(0);
        payService.save(payOrderDO);
        subOrderDO.setPayOrderGuid(payOrderDO.getGuid());
        if (subOrderDO.getOrderPrice().compareTo(BigDecimal.ZERO) == 0) {
            // 订单金额为0
            payOrderDO.setActuallyPayFee(BigDecimal.ZERO);
            payOrderDO.setOrderPrice(subOrderDO.getOrderPrice());
            payOrderDO.setTotalFee(subOrderDO.getTotalFee());
            payOrderDO.setStatus(Byte.valueOf("1"));
            payOrderDO.setErrorMsg("订单支付金额为0");
            subOrderDO.setStatus(SubOrderStatusEnum.IN_HANDEL.getCode());
            updatePayOrder(subOrderDO, payOrderDO);
            return getCurrentWxPayParamDTO(
                    PayOrderMapStruct.INSTANCE.do2DTO(payOrderDO),
                    payOrderDO.getGuid(),
                    subOrderDO.getGuid(),
                    null
            );
        }
        BigDecimal discountFee = BigDecimal.ZERO;
        if (subOrderDO.getUsedCoupon() && subOrderDO.getCouponGuid() != null) {
            // 验证优惠券是否可以使用
            CouponDO couponDO = couponService.checkCouponState(subOrderDO.getCouponGuid(), null);
            if (couponDO.getDiscountFee().compareTo(subOrderDO.getOrderPrice()) >= 0) {
                // 使用优惠券，并生成优惠券使用记录
                couponService.useCoupon(subOrderDO, couponDO);
                // 生成订单优惠记录
                orderDiscountService.saveOrUpdateCouponDiscount(subOrderDO, couponDO.getGuid(), OrderDiscountTypeEnum.COUPON.getType());
                subOrderDO.setTotalFee(BigDecimal.ZERO); // 应付金额为0
                subOrderDO.setTotalDiscountFee(subOrderDO.getOrderPrice()); // 优惠金额为商品总额
                subOrderDO.setActuallyPayFee(BigDecimal.ZERO);
                subOrderDO.setStatus(SubOrderStatusEnum.IN_HANDEL.getCode());

                payOrderDO.setTotalFee(BigDecimal.ZERO);
                payOrderDO.setActuallyPayFee(BigDecimal.ZERO);
                payOrderDO.setOrderPrice(subOrderDO.getOrderPrice());
                payOrderDO.setStatus(Byte.valueOf("1"));
                payOrderDO.setErrorMsg("订单支付金额为0");

                updatePayOrder(subOrderDO, payOrderDO);
                return getCurrentWxPayParamDTO(PayOrderMapStruct.INSTANCE.do2DTO(payOrderDO), payOrderDO.getGuid(), subOrderDO.getGuid(), null);
            }
            discountFee = couponDO.getDiscountFee();
        }
        BigDecimal totalFee = subOrderDO.getOrderPrice().subtract(discountFee);
        BigDecimal totalDiscountFee = subOrderDO.getOrderPrice().subtract(totalFee);
        UserLevelDO userLevelDO = new UserLevelDO();
        if (prepareToPayReqDTO.getUseCard()) {
            userLevelDO = userLevelService.getById(Long.parseLong(prepareToPayReqDTO.getPayTypeGuid()));
            userLevelDO.setVersion(prepareToPayReqDTO.getUserLevelVersion());
            // 会员卡所剩余额
            BigDecimal levelAmount = userLevelDO.getLevelAmount();
            // 订单原价
            BigDecimal orderPrice = subOrderDO.getOrderPrice();
            if (levelAmount.compareTo(BigDecimal.ZERO) <= 0) {
                throw new BizException("当前会员卡已无余额");
            }
            // 判断订单原价是否比折扣阈值小
            BigDecimal price = UserTypeEnum.getByCode(userLevelDO.getLevelCode()).getPrice();
            if (orderPrice.compareTo(price) > 0) {
                throw new BizException("会员余额不可用，订单金额大于折扣范围");
            }

            // 折扣后订单金额
            totalFee = totalFee.multiply(userLevelDO.getLevelDiscount())
                    .setScale(2, BigDecimal.ROUND_HALF_UP);
            totalDiscountFee = orderPrice.subtract(totalFee);
            subOrderDO.setTotalFee(totalFee);
            subOrderDO.setDiscount(userLevelDO.getLevelDiscount());
            subOrderDO.setTotalDiscountFee(orderPrice.subtract(totalFee));
            payOrderDO.setTotalFee(totalFee);
            BigDecimal actuallyPayFee;
            BigDecimal cardFee;
            if (levelAmount.compareTo(totalFee) >= 0) {
                // 会员卡余额比实际支付金额多 直接扣除余额，返回成功
                payOrderDO.setStatus(Byte.valueOf("1"));
                payOrderDO.setActuallyPayFee(BigDecimal.ZERO);

                // 修改订单信息
                subOrderDO.setStatus(SubOrderStatusEnum.IN_HANDEL.getCode());
                subOrderDO.setTotalDiscountFee(totalDiscountFee);
                subOrderDO.setCardFee(totalFee);
                // 修改会员余额，会员等级
                userLevelDO.setLevelAmount(levelAmount.subtract(totalFee));
                userService.userPay(userLevelDO, Long.parseLong(userDTO.getGuid()), prepareToPayReqDTO.getUserVersion());
                updatePayOrder(subOrderDO, payOrderDO);
                if (subOrderDO.getUsedCoupon() && subOrderDO.getCouponGuid() != null) {
                    CouponDO couponDO = couponService.getById(subOrderDO.getCouponGuid());
                    couponService.useCoupon(subOrderDO, couponDO);
                    // 生成订单优惠记录
                    orderDiscountService.saveOrUpdateCouponDiscount(
                            subOrderDO,
                            couponDO.getGuid(),
                            OrderDiscountTypeEnum.COUPON.getType()
                    );
                }
                return getCurrentWxPayParamDTO(
                        PayOrderMapStruct.INSTANCE.do2DTO(payOrderDO),
                        payOrderDO.getGuid(),
                        subOrderDO.getGuid(), userLevelDO.getGuid()
                );
            } else {
                // 卡余额比实际金额少
                actuallyPayFee = totalFee.subtract(levelAmount);
                payOrderDO.setStatus(Byte.valueOf("0"));
                // 修改订单信息
                cardFee = levelAmount;
            }
            payOrderDO.setActuallyPayFee(actuallyPayFee);
            payOrderDO.setUseCard(true);
            subOrderDO.setActuallyPayFee(actuallyPayFee);
            subOrderDO.setCardFee(cardFee);
            subOrderDO.setUserLevelGuid(userLevelDO.getGuid());
        } else {
            payOrderDO.setStatus(Byte.valueOf("0"));
            payOrderDO.setOrderPrice(subOrderDO.getOrderPrice());
            payOrderDO.setTotalFee(totalFee);
            payOrderDO.setActuallyPayFee(totalFee);
            payOrderDO.setUseCard(false);
            subOrderDO.setTotalFee(totalFee);
            subOrderDO.setDiscount(BigDecimal.ONE);
            subOrderDO.setActuallyPayFee(totalFee);
            subOrderDO.setCardFee(BigDecimal.ZERO);
        }
        subOrderDO.setTotalDiscountFee(totalDiscountFee);
        payService.updateById(payOrderDO);
        updateById(subOrderDO);
        return getCurrentWxPayParamDTO(
                PayOrderMapStruct.INSTANCE.do2DTO(payOrderDO),
                payOrderDO.getGuid(),
                subOrderDO.getGuid(),
                userLevelDO.getGuid()
        );
    }

    private void updatePayOrder(SubOrderDO subOrderDO, PayOrderDO payOrderDO) {
        payService.updateById(payOrderDO);
        updateById(subOrderDO);
        keFuService.sendMessage("order", String.valueOf(subOrderDO.getGuid()), new String[]{ThreadLocalCache.getOrg().getStoreName(), ThreadLocalCache.getOrg().getStoreName(), LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))}, 1);
        keFuService.sendMessage("text", "订单已支付成功", new String[]{ThreadLocalCache.getOrg().getStoreName(), ThreadLocalCache.getOrg().getStoreName(), LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))}, 1);
    }

    @Override
    @Transactional
    public TryPayRespDTO offlineTryPay(PrepareToPayReqDTO prepareToPayReqDTO) {
        if (prepareToPayReqDTO.getOfflinePayAmount() == null || prepareToPayReqDTO.getOfflinePayAmount().compareTo(BigDecimal.ZERO) <= 0) {
            throw new BizException("请输入正确的支付金额");
        }
        if (StringUtils.isEmpty(prepareToPayReqDTO.getOrganizationGuid())) {
            throw new BizException("非法的门店信息");
        }

        OrganizationDO organizationDO = organizationService.getById(Long.parseLong(prepareToPayReqDTO.getOrganizationGuid()));
        if (organizationDO == null) {
            throw new BizException("非法的门店信息");
        }
        UserDTO userDTO = ThreadLocalCache.get();
        PayOrderDO payOrderDO = PayOrderDO.builder()
                .orderPrice(prepareToPayReqDTO.getOfflinePayAmount())
                .useCard(prepareToPayReqDTO.getUseCard())
                .status(Byte.valueOf("0"))
                .type(Byte.valueOf("2"))
                .build();
        payOrderDO.setGuid(IdWorker.getId());
        payOrderDO.setOrganizationGuid(Long.parseLong(prepareToPayReqDTO.getOrganizationGuid()));
        payOrderDO.setUserGuid(Long.parseLong(userDTO.getGuid()));
        payService.save(payOrderDO);
        if (!prepareToPayReqDTO.getUseCard()) {
            payOrderDO.setTotalFee(prepareToPayReqDTO.getOfflinePayAmount());
            payOrderDO.setActuallyPayFee(prepareToPayReqDTO.getOfflinePayAmount());
            payService.updateById(payOrderDO);
            return getCurrentWxPayParamDTO(PayOrderMapStruct.INSTANCE.do2DTO(payOrderDO), payOrderDO.getGuid(), null, null);
        }

        payOrderDO.setAttach(prepareToPayReqDTO.getPayTypeGuid());
        UserLevelDO userLevelDO = userLevelService.getById(Long.parseLong(prepareToPayReqDTO.getPayTypeGuid()));
        userLevelDO.setVersion(prepareToPayReqDTO.getUserLevelVersion());
        // 会员卡所剩余额
        BigDecimal levelAmount = userLevelDO.getLevelAmount();
        // 订单原价
        if (levelAmount.compareTo(BigDecimal.ZERO) <= 0) {
            throw new BizException("当前会员卡已无余额");
        }
        // 判断订单原价是否比折扣阈值小
        BigDecimal price = UserTypeEnum.getByCode(userLevelDO.getLevelCode()).getPrice();
        if (prepareToPayReqDTO.getOfflinePayAmount().compareTo(price) > 0) {
            throw new BizException("会员余额不可用，订单金额大于折扣范围");
        }

        // 折扣后订单金额
        BigDecimal totalFee = prepareToPayReqDTO.getOfflinePayAmount().multiply(userLevelDO.getLevelDiscount()).setScale(2, BigDecimal.ROUND_HALF_UP);
        payOrderDO.setTotalFee(totalFee);
        payOrderDO.setUseCard(true);
        if (levelAmount.compareTo(totalFee) >= 0) {
            // 会员卡余额比实际支付金额多 直接扣除余额，返回成功
            payOrderDO.setStatus(Byte.valueOf("1"));
            payOrderDO.setActuallyPayFee(BigDecimal.ZERO);

            // 修改订单信息
            // 修改会员余额，会员等级
            userLevelDO.setLevelAmount(levelAmount.subtract(totalFee));
            userService.userPay(userLevelDO, Long.parseLong(userDTO.getGuid()), prepareToPayReqDTO.getUserVersion());
            payService.updateById(payOrderDO);
            SessionDO sessionDO = keFuService.createSession(Long.parseLong(userDTO.getGuid()), Long.parseLong(prepareToPayReqDTO.getOrganizationGuid()));
            String content = "线下订单支付成功，订单金额： %s元，折后金额： %s元，会员卡支付： %s元，微信支付： %s元";
            keFuService.sendMessage(sessionDO, "text",
                    String.format(content, payOrderDO.getOrderPrice(), payOrderDO.getTotalFee(), payOrderDO.getTotalFee(), BigDecimal.ZERO),
                    null, new String[]{
                            prepareToPayReqDTO.getOrganizationGuid(),
                            organizationDO.getStoreName(),
                            LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))
                    }, 3);
        } else {
            // 卡余额比实际金额少
            payOrderDO.setStatus(Byte.valueOf("0"));
            payOrderDO.setActuallyPayFee(totalFee.subtract(levelAmount));
            payService.updateById(payOrderDO);
        }
        return getCurrentWxPayParamDTO(PayOrderMapStruct.INSTANCE.do2DTO(payOrderDO), payOrderDO.getGuid(), null, userLevelDO.getGuid());
    }

    @Override
    public PayOrderDTO polling(String guid) {
        PayOrderDO byId = payService.getById(Long.parseLong(guid));
        return PayOrderMapStruct.INSTANCE.do2DTO(byId);
    }

    @Override
    public Boolean finish(String guid) {
        SubOrderDO byId = getById(guid);
        if (byId == null) {
            throw new BizException("订单不存在");
        }
        if (!Objects.equals(SubOrderStatusEnum.ALREADY_SEND_BACK.getCode(), byId.getStatus())) {
            throw new BizException("当前订单还未寄出，无法收货");
        }
        byId.setStatus(SubOrderStatusEnum.FINISHED.getCode());
        updateById(byId);
        return true;
    }

    @Transactional
    @Override
    public void orderCallBack(PayOrderDO payOrderDO, WxPayParamDTO wxPayParamDTO) {
        log.info("方法orderCallBack入参：{}", JacksonUtils.writeValueAsString(payOrderDO));
        SubOrderDO subOrderDO = getById(Long.parseLong(payOrderDO.getAttach()));
        if (subOrderDO == null) {
            throw new BizException("订单不存在");
        }
        subOrderDO.setPayOrderGuid(payOrderDO.getGuid());
        subOrderDO.setVersion(wxPayParamDTO.getSubOrderVersion());
        if (!Objects.equals(Byte.valueOf("1"), payOrderDO.getStatus())) { // 支付失败
            subOrderDO.setStatus(SubOrderStatusEnum.PAY_FAILED.getCode());
            if (!updateById(subOrderDO)) {
                log.info("商品订单【{}】，状态已发生变化，更新失败", subOrderDO.getGuid());
                throw new BizException("订单状态更新失败");
            }
            return;
        }
        subOrderDO.setStatus(SubOrderStatusEnum.IN_HANDEL.getCode());
        updateById(subOrderDO);
        if (payOrderDO.getUseCard()) {
            UserLevelDO userLevelDO = userLevelService.getById(subOrderDO.getUserLevelGuid());
            if (userLevelDO != null) {
                userLevelDO.setLevelAmount(userLevelDO.getLevelAmount().subtract(subOrderDO.getCardFee()));
                userService.userPay(userLevelDO, subOrderDO.getUserGuid(), wxPayParamDTO.getUserVersion());
            }
        }
        if (subOrderDO.getUsedCoupon() && subOrderDO.getCouponGuid() != null) {
            CouponDO couponDO = couponService.getById(subOrderDO.getCouponGuid());
            couponService.useCoupon(subOrderDO, couponDO);
            // 生成订单优惠记录
            orderDiscountService.saveOrUpdateCouponDiscount(subOrderDO, couponDO.getGuid(), OrderDiscountTypeEnum.COUPON.getType());
        }
        OrganizationDO org = ThreadLocalCache.getOrg();
        log.info("订单:【{}】支付成功，当前门店:{}", subOrderDO.getGuid(), org.getStoreName());
        keFuService.sendMessage("order", String.valueOf(subOrderDO.getGuid()), new String[]{org.getStoreName(), org.getStoreName(), LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))}, 1);
        keFuService.sendMessage("text", "订单已支付成功", new String[]{org.getStoreName(), org.getStoreName(), LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))}, 1);
    }

    @Override
    public KuaiDiDTO findKuaiDi(String guid) {
        KuaiDiDTO kuaiDiDTO = new KuaiDiDTO();
        SubOrderDO subOrderDO = baseMapper.selectById(guid);

        if (subOrderDO == null) {
            throw new BizException("订单不存在");
        }
        Optional.ofNullable(subOrderDO.getTrackingNumber()).filter(StringUtils::isNotBlank).ifPresent(e -> {
            KuaiDiResponse response = kuaiDiService.findByNum(e, subOrderDO.getDeliveryId());
            if (response.getResult() != null && !response.getResult()) {
                kuaiDiDTO.setCustmersMessage(response.getMessage());
            } else {
                kuaiDiDTO.setCustmers(response.getData());
            }

        });
        Optional.ofNullable(subOrderDO.getSysTrackingNumber()).filter(StringUtils::isNotBlank).ifPresent(e -> {
            KuaiDiResponse response = kuaiDiService.findByNum(e, subOrderDO.getSysDeliveryId());
            if (response.getResult() != null && !response.getResult()) {
                kuaiDiDTO.setKefusMessage(response.getMessage());
            } else {
                kuaiDiDTO.setKefus(response.getData());
            }
        });
        return kuaiDiDTO;
    }

    /**
     * @param type 2、待确认，5、待支付，9、待发货，11、待收货，不传表示查所有
     * @return List<SubOrderDTO>
     */
    @Override
    public List<SubOrderDTO> getAll(Integer type) {
        UserDTO userDTO = ThreadLocalCache.get();
        ValidateUserUtil.validateUser(userDTO);
        LambdaQueryWrapper<SubOrderDO> eq = new LambdaQueryWrapper<SubOrderDO>()
                .eq(SubOrderDO::getUserGuid, Long.parseLong(userDTO.getGuid()))
                .eq(SubOrderDO::getIsInCart, false);
        if (type != null) {
            switch (type) {
                case 2:
                    eq.in(SubOrderDO::getStatus,
                            Arrays.asList(SubOrderStatusEnum.WAIT_FOR_CHECKING.getCode(),
                                    SubOrderStatusEnum.PASSED_CHECK.getCode(),
                                    SubOrderStatusEnum.CHECK_FAILED.getCode()));
                    break;
                case 5:
                    eq.in(SubOrderDO::getStatus,
                            Arrays.asList(SubOrderStatusEnum.WAIT_FOR_PAYING.getCode(), SubOrderStatusEnum.PAY_FAILED.getCode()));
                    break;
                case 9:
                    eq.in(SubOrderDO::getStatus,
                            Arrays.asList(SubOrderStatusEnum.IN_HANDEL.getCode(),
                                    SubOrderStatusEnum.HANDEL_SUCCESS.getCode(),
                                    SubOrderStatusEnum.PAY_SUCCESS.getCode()));
                    break;
                case 11:
                    eq.eq(SubOrderDO::getStatus, SubOrderStatusEnum.ALREADY_SEND_BACK.getCode());
                    break;
                default:
                    break;
            }
        }
        List<SubOrderDO> list = list(eq);
        if (CollectionUtils.isEmpty(list)) {
            log.info("当前用户【{}】暂无订单", userDTO.getGuid());
            return Lists.newArrayList();
        }
        List<SubOrderDTO> subOrderDTOS = SubOrderMapStruct.INSTANCE.doList2DTOList(list.stream().sorted(Comparator.comparing(SubOrderDO::getGmtCreate).reversed()).collect(Collectors.toList()));

        for (SubOrderDTO subOrderDTO : subOrderDTOS) {
            getSubItems(subOrderDTO);
        }

        return subOrderDTOS;
    }

    @Override
    public SubOrderDTO getByGuid(String guid) {
        SubOrderDO subOrderDO = getById(guid);
        if (subOrderDO == null) {
            log.error("无该订单记录：【{}】", guid);
            return new SubOrderDTO();
        }
        SubOrderDTO subOrderDTO = SubOrderMapStruct.INSTANCE.do2DTO(subOrderDO);
        getSubItems(subOrderDTO);
        String receiveGuid = subOrderDTO.getReceiveGuid();
        if (!StringUtils.isEmpty(receiveGuid)) {
            UserReceiveInfoDO receiveInfo = userReceiveInfoService.getById(Long.parseLong(receiveGuid));
            if (receiveInfo != null) {
                String address = receiveInfo.getName() + " " + receiveInfo.getTel() + " " + receiveInfo.getAddress();
                subOrderDTO.setAddress(address);
            }
        }
        if (subOrderDO.getUsedCoupon() && subOrderDO.getCouponGuid() != null) {
            CouponRespDTO couponInfo = couponService.getCouponInfo(subOrderDO.getCouponGuid(),
                    Long.valueOf(subOrderDTO.getGuid()));
            subOrderDTO.setCouponRespDTO(couponInfo);
        }
        return subOrderDTO;

    }

    private void getSubItems(SubOrderDTO subOrderDTO) {
        List<GoodsItemDO> goodsItemDOS = goodsItemService.list(new LambdaQueryWrapper<GoodsItemDO>()
                .eq(GoodsItemDO::getSubOrderGuid, subOrderDTO.getGuid()));
        subOrderDTO.setGoodsItemDTOList(GoodsItemMapStruct.INSTANCE.doList2DTOList(goodsItemDOS));
        List<MenuDTO> fullMenuByProduct = menuService.getFullMenuByProduct(subOrderDTO);
        subOrderDTO.setItemsDTOList(fullMenuByProduct);
    }


    @Override
    public Boolean fillTrackingNumber(TrackingNumberDTO trackingNumberDTO) {
        SubOrderDO subOrderDO = this.getById(trackingNumberDTO.getOrderGuid());
        if (subOrderDO == null) {
            log.error("订单【{}】不存在：", trackingNumberDTO.getOrderGuid());
            log.error("快递公司：【{}】， 快递单号：【{}】", trackingNumberDTO.getDeliveryId(), trackingNumberDTO.getTrackingNumber());
            return false;
        }
        if (StringUtils.isAnyEmpty(trackingNumberDTO.getTrackingNumber(), trackingNumberDTO.getDeliveryId())) {
            throw new BizException("请填写正确的快递信息");
        }
        subOrderDO.setTrackingNumber(trackingNumberDTO.getTrackingNumber());
        subOrderDO.setDeliveryId(trackingNumberDTO.getDeliveryId());
        subOrderDO.setStatus(SubOrderStatusEnum.WAIT_FOR_CHECKING.getCode());
        boolean b = updateById(subOrderDO);
        String[] args = {
                subOrderDO.getGuid() + "",
                ThreadLocalCache.getOrg().getStoreName(),
                KuaiDiCompanyEnum.getByCode(trackingNumberDTO.getDeliveryId()).getName(),
                trackingNumberDTO.getTrackingNumber(),
                LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))
        };
        keFuService.sendMessage("text", "用户订单：" + subOrderDO.getGuid() + "\n快递单号： " + trackingNumberDTO.getTrackingNumber() + "， \n请注意查收", args, 2);
        return b;
    }

    @Override
    public Boolean orderNotify(String guid) {
        SubOrderDO subOrderDO = this.getById(Long.parseLong(guid));
        if (subOrderDO == null) {
            throw new BizException("订单不存在");
        }
        String first = "您的商品正在处理中";
        String remark = "商品处理中遇到问题，需要与您确认，请您主动与客服联系确认处理细节";
        sendMsg(subOrderDO, first, remark);
        return true;
    }

    @Override
    public Boolean updateOrder(SubOrderDTO subOrderDTO) {
        SubOrderDO oldOrder = this.getById(Long.valueOf(subOrderDTO.getGuid()));
        if (oldOrder.getStatus() != null &&
                (oldOrder.getStatus() >= SubOrderStatusEnum.IN_HANDEL.getCode() ||
                        Objects.equals(SubOrderStatusEnum.PAY_SUCCESS.getCode(), oldOrder.getStatus()))) {
            throw new BizException("订单已支付，无法修改订单信息");
        }
        List<GoodsItemDTO> goodsItemDTOList = subOrderDTO.getGoodsItemDTOList();
        if (CollectionUtils.isEmpty(goodsItemDTOList)) {
            return false;
        }
        List<GoodsItemDO> goodsItemDOS = GoodsItemMapStruct.INSTANCE.dtoList2DOList(goodsItemDTOList);
        goodsItemDOS.forEach(e -> {
            if (e.getPrice() == null) {
                throw new BizException("还有产品暂未定价，请定价后保存");
            }
            e.setIsUpdatedPrice(Boolean.TRUE);
            e.setSubOrderGuid(Long.parseLong(subOrderDTO.getGuid()));
        });
        goodsItemService.updateBatchById(goodsItemDOS);
        BigDecimal bigDecimal = goodsItemDOS.stream().map((e) -> e.getPrice().multiply(new BigDecimal(e.getCount()))).reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
        SubOrderDO subOrderDO = SubOrderMapStruct.INSTANCE.dto2DO(subOrderDTO);
        subOrderDO.setOrderPrice(bigDecimal);
        subOrderDO.setStatus(SubOrderStatusEnum.WAIT_FOR_PAYING.getCode());
        subOrderDO.setIsChecked(true);
        boolean flag = this.updateById(subOrderDO);
        String first = "您的订单状态发生变化";
        String remark = "请前往小程序确认订单\n如有疑问，请联系客服人员";
        SubOrderDO dbOrder = getById(subOrderDO.getGuid());
        sendMsg(dbOrder, first, remark);
        return flag;
    }

    @Override
    public Boolean updateOrderKuaidi(KuaiDiNumberDTO kuaiDiNumberDTO) {
        SubOrderDO subOrderDO = baseMapper.selectById(kuaiDiNumberDTO.getSubOrderGuid());
        if (subOrderDO == null) {
            throw new BizException("订单不存在");
        }
        subOrderDO.setSysDeliveryId(kuaiDiNumberDTO.getCompanyCode());
        subOrderDO.setSysTrackingNumber(kuaiDiNumberDTO.getNumber());
        subOrderDO.setStatus(SubOrderStatusEnum.ALREADY_SEND_BACK.getCode());
        baseMapper.updateById(subOrderDO);
        String first = "您的商品已寄出";
        String remark = "请前往小程序确认订单\n如有疑问，请联系客服人员";
        sendMsg(subOrderDO, first, remark);
        return true;
    }

    @Override
    public Boolean checkReceive(String guid) {
        SubOrderDO subOrderDO = baseMapper.selectById(guid);
        if (subOrderDO == null) {
            throw new BizException("订单不存在");
        }
        subOrderDO.setStatus(SubOrderStatusEnum.HANDEL_SUCCESS.getCode());
        this.updateById(subOrderDO);
        UserReceiveInfoDO receiveInfo = userReceiveInfoService.getById(subOrderDO.getReceiveInfoGuid());
        String productName = subOrderDO.getProductName();
        String receiveAddress = receiveInfo.getAddress();
        String name = receiveInfo.getName();
        String tel = receiveInfo.getTel();
        Long userGuid = subOrderDO.getUserGuid();
        UserDO userDO = userService.getById(userGuid);
        String openId = userDO.getOpenId();
        List<String> list = Arrays.asList(productName, name, tel, receiveAddress);
        String first = "您的商品已处理完成，准备寄出";
        String remark = "请前往小程序与客服确认处理结果及收货信息";
        wxMaPortalService.sendTemplateMsg(SEND_MSG_FROM_ID, list, openId, first, remark);
        return true;
    }

    @Override
    public Boolean receive(String guid) {
        SubOrderDO subOrderDO = this.getById(guid);
        if (subOrderDO == null) {
            throw new BizException("订单不存在");
        }
        String first = "商家已收到商品";
        String remark = "请联系客服确认订单最终价格";
        sendMsg(subOrderDO, first, remark);
        return true;
    }

    @Override
    public OrderCountDTO getCount() {
        Long userGuid = Long.parseLong(ThreadLocalCache.get().getGuid());
        int forCheckingCount = count(new LambdaQueryWrapper<SubOrderDO>().eq(SubOrderDO::getUserGuid, userGuid)
                .in(SubOrderDO::getStatus,
                        Arrays.asList(SubOrderStatusEnum.WAIT_FOR_CHECKING.getCode(),
                                SubOrderStatusEnum.PASSED_CHECK.getCode(),
                                SubOrderStatusEnum.CHECK_FAILED.getCode())));
        int forPayingCount = count(new LambdaQueryWrapper<SubOrderDO>().eq(SubOrderDO::getUserGuid, userGuid)
                .in(SubOrderDO::getStatus,
                        Arrays.asList(SubOrderStatusEnum.WAIT_FOR_PAYING.getCode(), SubOrderStatusEnum.PAY_FAILED.getCode())));
        int forSendingCount = count(new LambdaQueryWrapper<SubOrderDO>().eq(SubOrderDO::getUserGuid, userGuid)
                .in(SubOrderDO::getStatus,
                        Arrays.asList(SubOrderStatusEnum.IN_HANDEL.getCode(),
                                SubOrderStatusEnum.HANDEL_SUCCESS.getCode(),
                                SubOrderStatusEnum.PAY_SUCCESS.getCode())));
        int forReceivingCount = count(new LambdaQueryWrapper<SubOrderDO>().eq(SubOrderDO::getUserGuid, userGuid)
                .eq(SubOrderDO::getStatus, SubOrderStatusEnum.ALREADY_SEND_BACK.getCode()));
        return OrderCountDTO.builder()
                .forCheckingCount(forCheckingCount)
                .forPayingCount(forPayingCount)
                .forReceivingCount(forReceivingCount)
                .forSendingCount(forSendingCount)
                .build();
    }

    /**
     * couponNo 优惠券编号
     * 1.判断订单状态能否更改或使用优惠券
     * 2.empty 不使用优惠券，remove orderDiscount, condition: orderDiscount type =1 && orderGuid && couponNo
     * 3.判断订单商品能否使用优惠券
     * 4.检查优惠券状态以及用户能否使用优惠券
     * 5.判断订单是否之前有优惠券，如果有，是否是修改了优惠券
     * 6.更新订单信息
     *
     * @param couponAddReqDTO 优惠券请求入参
     * @return
     */
    @Override
    public Boolean addOrUpdateCoupon(CouponAddReqDTO couponAddReqDTO) {
        SubOrderDTO subOrderDTO = this.getByGuid(couponAddReqDTO.getSubOrderGuid());
        SubOrderDO subOrderDO = SubOrderMapStruct.INSTANCE.dto2DO(subOrderDTO);
        if (!Objects.equals(SubOrderStatusEnum.PAY_FAILED.getCode(), subOrderDTO.getStatus()) &&
                !Objects.equals(SubOrderStatusEnum.WAIT_FOR_PAYING.getCode(), subOrderDTO.getStatus())) { // 订单不是待支付状态不允许修改优惠券
            throw new BizException("当前订单状态不允许修改优惠券信息");
        }
        if (StringUtils.isEmpty(couponAddReqDTO.getCouponNo())) { // 不使用优惠券
            subOrderDO.setUsedCoupon(false);
            return this.updateById(subOrderDO);
        }
        List<String> productGuidList = subOrderDTO.getGoodsItemDTOList().stream()
                .map(GoodsItemDTO::getProductGuid)
                .collect(Collectors.toList());
        if (productService.count(new LambdaQueryWrapper<ProductDO>()
                .in(ProductDO::getGuid, productGuidList)
                .eq(ProductDO::getIsJoinActivity, true))
                != productGuidList.size()) { // 检查订单商品能否使用优惠券
            throw new BizException("订单商品不能使用优惠券");
        }
        // 检查优惠券使用状态
        CouponDO couponDO = couponService.checkCouponState(null, couponAddReqDTO.getCouponNo());
        CouponRespDTO couponRespDTO = subOrderDTO.getCouponRespDTO();
        if (subOrderDTO.getUsedCoupon() && couponRespDTO != null) { //订单已经添加过优惠券
            if (!Objects.equals(couponDO.getCouponNo(), couponRespDTO.getCouponNo())) { // 修改了优惠券信息
                subOrderDO.setCouponGuid(couponDO.getGuid());
            }
        } else { // 订单还未添加优惠券
            subOrderDO.setCouponGuid(couponDO.getGuid());
        }
        subOrderDO.setUsedCoupon(Boolean.TRUE);
        return this.updateById(subOrderDO);
    }

    private void sendMsg(SubOrderDO subOrderDO, String first, String remark) {
        Long userGuid = subOrderDO.getUserGuid();
        UserDO userDO = userService.getById(userGuid);
        Long organizationGuid = subOrderDO.getOrganizationGuid();
        OrganizationDO org = organizationService.getById(organizationGuid);
        String openId = userDO.getOpenId();
        String orderGuid = String.valueOf(subOrderDO.getGuid());
        String productName = subOrderDO.getProductName();
        String amount = subOrderDO.getOrderPrice() + " 元";
        List<String> params = Arrays.asList(org.getStoreName(), productName, orderGuid, amount, SubOrderStatusEnum.getByCode(subOrderDO.getStatus()).getMessage());
        wxMaPortalService.sendTemplateMsg(ORDER_CHANGE_FROM_ID, params, openId, first, remark);
    }

    private TryPayRespDTO getCurrentWxPayParamDTO(PayOrderDTO payOrderDTO, Long payOrderGuid, Long subOrderGuid, Long userLevelGuid) {
        UserDTO userDTO = ThreadLocalCache.get();
        UserDO userDO = new UserDO();
        userDO.setGuid(Long.parseLong(userDTO.getGuid()));
        userDO.setVersion(userDTO.getUserVersion());
        UserLevelDO userLevelDO = null;
        if (userLevelGuid != null) {
            userLevelDO = userLevelService.getOne(
                    new LambdaQueryWrapper<UserLevelDO>()
                            .eq(UserLevelDO::getGuid, userLevelGuid));
        }
        SubOrderDO subOrderDO = null;
        if (subOrderGuid != null) {
            subOrderDO = this.getOne(
                    new LambdaQueryWrapper<SubOrderDO>()
                            .select(SubOrderDO::getGuid, SubOrderDO::getVersion)
                            .eq(SubOrderDO::getGuid, subOrderGuid));
        }
        PayOrderDO payOrderDO = payService.getOne(
                new LambdaQueryWrapper<PayOrderDO>()
                        .select(PayOrderDO::getGuid, PayOrderDO::getVersion)
                        .eq(PayOrderDO::getGuid, payOrderGuid));
        return TryPayRespDTO.builder()
                .payOrderDTO(payOrderDTO)
                .wxPayParamDTO(
                        WxPayParamDTO.INSTANCE(payOrderDO, userLevelDO, userDO, subOrderDO))
                .build();
    }
}
